/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */



package chatservidordistribudios;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 *
 * @author Kleyvert
 */
public class Servidor implements Runnable
{
   // Atributos:

   private int puerto;
   private Socket conexion;
   private ServerSocket servidor;
   private DataOutputStream msnSalida;
   private DataInputStream msnEntrada;
   private ArrayList<String> usuarioMensaje = new ArrayList<String>();
   public ArrayList<Socket> SalidaStrem = new ArrayList<Socket>();
   public static ArrayList<Socket> ConexionTodos = new ArrayList<Socket>();

   // CONSTRUCTOR:

   public Servidor(int puerto) throws IOException
   {
        this.puerto = puerto;
        this.setUsuarioMensaje(new ArrayList<String>());
    }


   // getter y setter:
    public DataOutputStream getMsnSalida() {
        return msnSalida;
    }
    public void setMsnSalida(DataOutputStream msnSalida) {
        this.msnSalida = msnSalida;
    }
    public DataInputStream getMsnEntrada() {
        return msnEntrada;
    }
    public void setMsnEntrada(DataInputStream msnEntrada) {
        this.msnEntrada = msnEntrada;
    }
    public ArrayList<String> getUsuarioMensaje() {
        return usuarioMensaje;
    }
    public void setUsuarioMensaje(ArrayList<String> usuarioMensaje) {
        this.usuarioMensaje = usuarioMensaje;
    }

   // levantar servidor con el socket
   public void LevantarServidor(int puerto) throws IOException{
        
            // Instanciamos un objeto del tipo ServerSocket.
            servidor = new ServerSocket(puerto);
            System.out.println("Escuchando por: "+servidor);

            // colocamos el servidor como un "Demonio" escuchando conexiones
            while (true) {
              try
              {
                    conexion = servidor.accept();
                    System.out.println("Conectando por: "+ conexion.getInetAddress());
                    // Crear un objeto para escribir mensajes al cliente
                    this.setMsnSalida(new DataOutputStream(conexion.getOutputStream()));
                    this.setMsnEntrada(new DataInputStream(conexion.getInputStream()));
                    // guardamos el Stream en un ArrayList, para no tener
                    SalidaStrem.add(conexion);
                    ConexionTodos.add(conexion);
                    // Leemos la salida del mensaje y el identificador
                    int codigo = this.getMsnEntrada().readInt();
                    String mensaje = this.getMsnEntrada().readUTF();
                    // guardamos en la lista
                    if ( codigo == 1 ){
                        this.getUsuarioMensaje().add(mensaje);
                        this.EnviarMensaje(mensaje, codigo);
                    }
                    // Instanciamos el Servidor Hilo: un hilo por cada conexion con un CLiente:
                    // Hilo que atiende al ususario recien conectado:
                   Thread serverHilo = new Thread (new ServidorHilo(this,conexion));
                   serverHilo.start();                
                }
                // procesar la conexión.
                catch (EOFException e)
                {
                    System.err.println("El servidor no pudo seguir en ejecucion");
                } 
                
            }    
   }

   // Enviar mensaje a todos los cliente de una determinada sala:
   public void EnviarMensaje(String mensajeParticular, int identificador)
   {
       // Sincronizamos la conexion de cada cliente:
       synchronized(SalidaStrem)
       {
         //System.out.println("Enviando el mensaje del cliente: "+mensajeParticular);
         // recorremos la tabla de hash decada saLa
            for (int i=0; i<=SalidaStrem.size()-1; i++)
            {
             try
             {             
                // Escribimos el mensaje de un determinado cliente de una sala,
                // a TODOS los de la misma SALA:
                for (int num=0 ;num <= getUsuarioMensaje().size()-1; num++){
                    //Obtener el OutPutStream (mensaje que escribiremos a los CLientes):
                     DataOutputStream salida = new DataOutputStream(SalidaStrem.get(i).getOutputStream());                
                     if (identificador == 1){
                         salida.writeInt(identificador);
                         salida.writeUTF(getUsuarioMensaje().get(num).toString());
                     }
                }
                DataOutputStream salida = new DataOutputStream(SalidaStrem.get(i).getOutputStream());
                if (identificador == 2){
                    salida.writeInt(identificador);
                    salida.writeUTF(mensajeParticular);
                }
                         
             }
                catch(IOException e)
                {
                    System.out.println("Fallo la escritura de mensajes: "+e);
                }
            }
       }
   }

   // Metodo para recorrer la lista de hilos y enviar mensajes a todas las salas
   public void EnviarMensajeaTodos(String mensaje, int identificador){
      // Sincronizamos la conexion de cada cliente:
       synchronized(ConexionTodos)
       {
         //System.out.println("Enviando el mensaje del cliente: "+mensajeParticular);
         // recorremos la tabla de hash decada saLa
            for (int i=0; i<=ConexionTodos.size()-1; i++)
            {
             try
             {
                // Escribimos el mensaje de un determinado cliente de una sala,
                // a TODOS los de la misma SALA:
                for (int num=0 ;num <= getUsuarioMensaje().size()-1; num++){
                    //Obtener el OutPutStream (mensaje que escribiremos a los CLientes):
                     DataOutputStream salida = new DataOutputStream(ConexionTodos.get(i).getOutputStream());
                     if (identificador == 1){
                         salida.writeInt(identificador);
                         salida.writeUTF(getUsuarioMensaje().get(num).toString());
                     }
                }
                DataOutputStream salida = new DataOutputStream(ConexionTodos.get(i).getOutputStream());
                if (identificador == 4){
                    salida.writeInt(identificador);
                    salida.writeUTF(mensaje);
                }

             }
                catch(IOException e)
                {
                    System.out.println("Fallo la escritura de mensajes: "+e);
                }
            }
       }
      
   }
   // Este metodo remueve la conexion existente CONTRA UN CLIENTE.
   public void RemoverConexion(String mensaje)
   {
       String usuario=" ";
       synchronized(ConexionTodos)
       {
           System.out.println("Removiendo la conexion: " + mensaje);
           for (int i=0; i<=SalidaStrem.size()-1; i++)
           {
             try{
                for (int num = 0; num <= getUsuarioMensaje().size()-1; num++){
                     usuario = getUsuarioMensaje().get(num).toString();

                     if (usuario == mensaje){
                         //SalidaStrem.get(num).close();
                         this.getUsuarioMensaje().remove(num);
                     }
                }
             }catch(Exception e){
                    System.out.println("Fallo la escritura de mensajes: "+e);
              }
   
           
       }
     }

   }
   
    // Metodo para cerrar la conexiones tanto de "espera" como de "salida" DE CLIENTES,
   //dado un servidor de UNA SALA:
   public void cerrarConexion()
   {
        try {         
           // Cerramos la conexion del socket generico
           conexion.close();
           System.out.println("Conexion finalizada");
        }
        catch( IOException excepcionES )
        {
              excepcionES.printStackTrace();
        }
      
   }

    public void run()
    {
        try {
            this.LevantarServidor(puerto);
        } catch (IOException ex) {
            Logger.getLogger(Servidor.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}